package org.example.executor;


import java.util.HashSet;
import java.util.concurrent.*;

public class MyExecutor {
    private volatile int corePollSize;
    private volatile int maximumPoolSize;
    private volatile int keepAliveTime;
    private volatile BlockingQueue<Runnable> task;
    private volatile ThreadFactory threadFactory;
    private volatile RejectedExecutionHandler handler;
    private volatile TimeUnit unit;


    private volatile HashSet<Worker> workers = new HashSet<>();

    private volatile int threadNum = 0;

    public static void main(String[] args) {
        MyExecutor myExecutor = new MyExecutor(1, 1, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadFactory() {
            int i = 0;
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("myThread"+i++);
                return thread;
            }
        }, new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("执行拒绝策略");
            }
        });
        System.out.println(111);
        myExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(1);
                System.out.println(Thread.currentThread().getName());
            }
        });

        System.out.println(222);
        myExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(1);
                System.out.println(Thread.currentThread().getName());
            }
        });

        System.out.println(333);
        myExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(1);
                System.out.println(Thread.currentThread().getName());
            }
        });

        System.out.println(myExecutor.threadNum);
    }


    public void execute(Runnable runnable) {
        if (threadNum < corePollSize) {
            addWorker(runnable, true);
            return;
        } else if (task.offer(runnable)) {
            System.out.println("入队");
            return;
        } else if (threadNum < maximumPoolSize) {
            addWorker(runnable, false);
            return;
        }
        handler.rejectedExecution(runnable, null);

    }

    private void addWorker(Runnable runnable, boolean b) {
        Worker worker = new Worker(runnable);
        Thread t = worker.thread;
        workers.add(worker);
        threadNum++;
        t.start();
    }


    private void doWork(Worker worker) {
        Runnable task = worker.task;
        Thread thread = worker.thread;
        try {
            while (!thread.isInterrupted() && (task != null || (task = getTask()) != null)) {
                task.run();
                task = null;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            worker.remove();
        }
    }

    private Runnable getTask() throws InterruptedException {
        if (threadNum <= corePollSize) {
            return task.take();
        } else {
            return task.poll(keepAliveTime, unit);
        }
    }


    private class Worker implements Runnable {
        private Runnable task;

        Thread thread;

        public Worker(Runnable task) {
            this.task = task;
            this.thread = threadFactory.newThread(this);
        }

        @Override
        public void run() {
            doWork(this);
        }

        public void remove() {
            workers.remove(this);
            threadNum--;
        }

    }


    public MyExecutor(int corePollSize, int maximumPoolSize, int keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> task, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        this.corePollSize = corePollSize;
        this.maximumPoolSize = maximumPoolSize;
        this.keepAliveTime = keepAliveTime;
        this.task = task;
        this.threadFactory = threadFactory;
        this.handler = handler;
        this.unit = unit;
    }
}